//=============================================================================
//
//   Copyright (C) 2020 All rights reserved.
//
//   Filename:		dof.rs
//
//   Author: Wang Zhecheng - wangzhecheng@yeah.net
//
//   Date: 2020-10-08 01:06
//
//   Last Editors: Wang Zhecheng - wangzhecheng@yeah.net
//
//   Last modified:	2020-11-06 18:27
//
//   Description:
//
//=============================================================================
use crate::arith::time_func::time_func_interface::TimeFuncInterface;
use crate::Real;
use crate::util::Indexable;

///
/// enum BcType
///
#[derive(Debug, PartialEq)]
pub enum BcType {
    None,
    Dirichlet(Real),
    Neumann(Real),
}

///
/// dof struct
///
#[derive(Debug)]
pub struct Dof {
    id: Option<usize>,
    bc_type: BcType,
    value: Real,
    d_value: Real,
    dd_value: Real,
    bdry_value: Real,
    load: Real,
    time_func: Option<Box<dyn TimeFuncInterface>>,
}

impl Dof {
    /// get total value at previous time step
    #[inline]
    fn get_value(&self) -> Real {
        self.value
    }
    /// self increment operator of absolute value at previous time step
    #[inline]
    fn add_assign_value(&mut self, rhs: Real) {
        self.value += rhs;
    }
    /// get delta value at current time step
    #[inline]
    fn get_value_in_curr_tstep(&self) -> Real {
        self.d_value
    }
    /// self increment operator of delta value at current time step
    #[inline]
    fn add_assign_value_in_curr_tstep(&mut self, rhs: Real) {
        self.d_value += rhs;
    }
    /// get delta value at current time step
    #[inline]
    fn get_value_in_curr_iter(&self) -> Real {
        self.dd_value
    }
    /// set delta value at current time step
    #[inline]
    fn set_value_in_curr_iter(&mut self, rhs: Real) {
        self.dd_value = rhs;
    }
    /// get total value at current time/iteration
    #[inline]
    fn get_curr_value(&self) -> Real {
        self.get_value() + self.get_value_in_curr_tstep()
    }
    /// get absolute load
    #[inline]
    fn get_external_load(&self) -> Real {
        self.load
    }
    /// set absolute load
    #[inline]
    fn set_external_load(&mut self, rhs: Real) {
        self.load = rhs;
    }
    /// if is Dirichlet
    #[inline]
    fn get_dirichlet(&self) -> (bool, Real) {
        match &self.bc_type {
            BcType::Dirichlet(v) => (true, *v),
            _ => (false, 0 as Real),
        }
    }
    /// if is Neumann
    #[inline]
    fn get_neumann(&self) -> (bool, Real) {
        match &self.bc_type {
            BcType::Neumann(v) => (true, *v),
            _ => (false, 0 as Real),
        }
    }
    /// if is free boundary
    #[inline]
    fn if_free(&self) -> bool {
        match &self.bc_type {
            BcType::None => true,
            _ => false,
        }
    }
}

///
/// default
///
impl Default for Dof {
    #[inline]
    fn default() -> Self {
        Self {
            id: None,
            bc_type: BcType::None,
            value: 0 as Real,
            d_value: 0 as Real,
            dd_value: 0 as Real,
            bdry_value: 0 as Real,
            load: 0 as Real,
            time_func: None,
        }
    }
}

indexable_impl!(Dof);

#[test]
fn index() {
    let mut dof = Dof::default();
    dof.set_index(32);
    assert_eq!(dof.get_index().unwrap(), 32);
}
